+/*
+ Copyright (C) 2024 Rubén Beltrán del Río
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see https://map.tranquil.systems.
+ */
import Cocoa
import SwiftUI
class MapTextEditorController: NSViewController {
@Binding var document: MapDocument
+ var highlightRanges: [Range<String.Index>] {
+ didSet {
+ updateHighlights()
+ }
+ }
+
+ var selectedRange: Int {
+ didSet {
+ updateHighlights()
+ focusOnResult()
+ }
+ }
+
let onChange: () -> Void
private let vertexRegex = MapParsingPatterns.vertex
private let changeDebouncer: Debouncer = Debouncer(seconds: 1)
- init(document: Binding<MapDocument>, onChange: @escaping () -> Void) {
+ init(
+ document: Binding<MapDocument>, highlightRanges: [Range<String.Index>], selectedRange: Int,
+ onChange: @escaping () -> Void
+ ) {
self._document = document
self.onChange = onChange
+ self.highlightRanges = highlightRanges
+ self.selectedRange = selectedRange
super.init(nibName: nil, bundle: nil)
}
override func viewDidAppear() {
self.view.window?.makeFirstResponder(self.view)
+ updateHighlights()
+ }
+
+ private var textView: NSTextView? {
+ return (view as? NSScrollView)?.documentView as? NSTextView
+ }
+
+ private func updateHighlights() {
+ if let textView {
+ if let textStorage = textView.textStorage {
+ textStorage.removeAttribute(
+ .backgroundColor, range: NSRange(location: 0, length: textStorage.length))
+
+ for range in highlightRanges {
+ let nsRange = NSRange(range, in: textStorage.string)
+
+ textStorage.addAttribute(.backgroundColor, value: NSColor.syntax.match, range: nsRange)
+ }
+
+ textView.needsDisplay = true
+
+ }
+ }
+ }
+
+ private func focusOnResult() {
+ if let textView {
+ if let textStorage = textView.textStorage {
+ if selectedRange < highlightRanges.count {
+ let range = highlightRanges[selectedRange]
+ let nsRange = NSRange(range, in: textStorage.string)
+ textView.scrollRangeToVisible(nsRange)
+ textView.selectedRange = nsRange
+ }
+ }
+ }
+ }
+
+ private func setSelectionColor() {
+ guard let textView = self.textView else { return }
+
+ var selectedTextAttributes = textView.selectedTextAttributes
+ selectedTextAttributes[.backgroundColor] = NSColor.yellow.withAlphaComponent(0.3)
+ textView.selectedTextAttributes = selectedTextAttributes
}
}
struct MapTextEditor: NSViewControllerRepresentable {
@Binding var document: MapDocument
+ var highlightRanges: [Range<String.Index>]
+ var selectedRange: Int
var onChange: () -> Void = {}
func makeNSViewController(
context: NSViewControllerRepresentableContext<MapTextEditor>
) -> MapTextEditorController {
- return MapTextEditorController(document: $document, onChange: onChange)
+ return MapTextEditorController(
+ document: $document, highlightRanges: highlightRanges, selectedRange: selectedRange,
+ onChange: onChange)
}
func updateNSViewController(
_ nsViewController: MapTextEditorController,
context: NSViewControllerRepresentableContext<MapTextEditor>
- ) {}
+ ) {
+ nsViewController.highlightRanges = highlightRanges
+ nsViewController.selectedRange = selectedRange
+ }
}